package org.springframework.security.config.annotation.web.configuration;

import jakarta.servlet.Filter;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.annotation.Order;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.security.access.expression.SecurityExpressionHandler;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.SecurityConfigurer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor;
import org.springframework.security.context.DelegatingApplicationListener;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.WebInvocationPrivilegeEvaluator;
import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author Dillon
 * @date 2024/7/15
 * @slogan 致敬大师 致敬未来的你
 * @desc springSecurity核心配置类，可以通过该配置类配置多个过滤器链
 * {@link HttpSecurity} 可以配置多个
 * {@link WebSecurity} 配置一个webSecurity 并且将多个 springSecurityFilterChain放到 webSecurity 中
 * 最终构建出 FilterChainProxy 对象 ，而 FilterChainProxy 对象则有delegationFilterChain 执行
 * 详见配置类 {@link SecurityFilterAutoConfiguration # securityFilterChainRegistration} spring中配置
 *
 */
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {

	/**
	 * WebSecurity 配置类
	 */
	private WebSecurity webSecurity;

	/**
	 * 是否允许debug 默认不允许
	 */
	private Boolean debugEnabled;

	private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;

	/**
	 * 自定义配置的 过滤器链
	 */
	private List<SecurityFilterChain> securityFilterChains = Collections.emptyList();

	/**
	 * 自定义设置的 WebSecurity 配置项
	 */
	private List<WebSecurityCustomizer> webSecurityCustomizers = Collections.emptyList();

	/**
	 * 类加载器
	 */
	private ClassLoader beanClassLoader;

	/**
	 * 自定义配置的后置处理器
	 */
	@Autowired(required = false)
	private ObjectPostProcessor<Object> objectObjectPostProcessor;

	/**
	 * HttpSecurity 对象
	 */
	@Autowired(required = false)
	private HttpSecurity httpSecurity;

	/**
	 * 代理事件监听对象注册
	 * @return 事件监听对象
	 */
	@Bean
	public static DelegatingApplicationListener delegatingApplicationListener() {
		return new DelegatingApplicationListener();
	}

	/**
	 * security的表达式处理器列表注册
	 * @return SecurityExpressionHandler
	 */
	@Bean
	@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
		return this.webSecurity.getExpressionHandler();
	}

	/**
	 * 核心，构建 FilterChainProxy 对象
	 * @return FilterChainProxy
	 * @throws Exception 构建异常
	 * springSecurityFilterChain bean名称，为了让代理类生效
	 */
	@Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public Filter springSecurityFilterChain() throws Exception {
		// 获取配置的过滤器链列表，并挨个调用build方法，初始化
		boolean hasFilterChain = !this.securityFilterChains.isEmpty();
		if (!hasFilterChain) {
			this.webSecurity.addSecurityFilterChainBuilder(() -> {
				this.httpSecurity.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated());
				this.httpSecurity.formLogin(Customizer.withDefaults());
				this.httpSecurity.httpBasic(Customizer.withDefaults());
				return this.httpSecurity.build();
			});
		}
		// 将过滤器链 增加到webSecurity 中
		for (SecurityFilterChain securityFilterChain : this.securityFilterChains) {
			this.webSecurity.addSecurityFilterChainBuilder(() -> securityFilterChain);
		}
		// 执行自定义的 webSecurity 配置
		for (WebSecurityCustomizer customizer : this.webSecurityCustomizers) {
			customizer.customize(this.webSecurity);
		}
		// 构建 webSecurity
		return this.webSecurity.build();
	}

	/**
	 * Creates the {@link WebInvocationPrivilegeEvaluator} that is necessary to evaluate
	 * privileges for a given web URI
	 * @return the {@link WebInvocationPrivilegeEvaluator}
	 */
	@Bean
	@DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
	public WebInvocationPrivilegeEvaluator privilegeEvaluator() {
		return this.webSecurity.getPrivilegeEvaluator();
	}

	/**
	 * 将配置的webSecurity自定义配置应用到 webSecurity 实例上
	 * @param objectPostProcessor 对象后处理器
	 * @param beanFactory bean工厂
	 * @throws Exception 执行异常
	 */
	@Autowired(required = false)
	public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
			ConfigurableListableBeanFactory beanFactory) throws Exception {
		// 后验处理 webSecurity 以及debug属性支持设置
		this.webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
		if (this.debugEnabled != null) {
			this.webSecurity.debug(this.debugEnabled);
		}

		// 获取自定义配置的 webSecurity 并将之应用到webSecurity 中
		List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers =
				new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory).getWebSecurityConfigurers();
		webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);
		Integer previousOrder = null;
		Object previousConfig = null;
		for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
			Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
			if (previousOrder != null && previousOrder.equals(order)) {
				throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order
						+ " was already used on " + previousConfig + ", so it cannot be used on " + config + " too.");
			}
			previousOrder = order;
			previousConfig = config;
		}
		for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
			this.webSecurity.apply(webSecurityConfigurer);
		}
		this.webSecurityConfigurers = webSecurityConfigurers;
	}

	/**
	 * 通过spring进行bean注入
	 * @param securityFilterChains 自定义的过滤器链配置
	 */
	@Autowired(required = false)
	void setFilterChains(List<SecurityFilterChain> securityFilterChains) {
		this.securityFilterChains = securityFilterChains;
	}

	/**
	 * 通过spring进行bean注入
	 * @param webSecurityCustomizers 自定义设置的 WebSecurity 配置项
	 */
	@Autowired(required = false)
	void setWebSecurityCustomizers(List<WebSecurityCustomizer> webSecurityCustomizers) {
		this.webSecurityCustomizers = webSecurityCustomizers;
	}

	@Bean
	public static BeanFactoryPostProcessor conversionServicePostProcessor() {
		return new RsaKeyConversionServicePostProcessor();
	}

	/**
	 * 获取 EnableWebSecurity 注解配置信息
	 * @param importMetadata spring 注解元数据封装类
	 */
	@Override
	public void setImportMetadata(AnnotationMetadata importMetadata) {
		// 尝试获取 EnableWebSecurity 注解 并获取里面的配置是否启用 debug配置
		Map<String, Object> enableWebSecurityAttrMap = importMetadata.getAnnotationAttributes(EnableWebSecurity.class.getName());
		AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes.fromMap(enableWebSecurityAttrMap);
		this.debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
		if (this.webSecurity != null) {
			this.webSecurity.debug(this.debugEnabled);
		}
	}

	/**
	 * 设置bean类加载器
	 * @param classLoader classLoader
	 */
	@Override
	public void setBeanClassLoader(ClassLoader classLoader) {
		this.beanClassLoader = classLoader;
	}

	/**
	 * A custom version of the Spring provided AnnotationAwareOrderComparator that uses
	 * {@link AnnotationUtils#findAnnotation(Class, Class)} to look on super class
	 * instances for the {@link Order} annotation.
	 *
	 * @author Rob Winch
	 * @since 3.2
	 */
	private static class AnnotationAwareOrderComparator extends OrderComparator {

		private static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();

		@Override
		protected int getOrder(Object obj) {
			return lookupOrder(obj);
		}

		private static int lookupOrder(Object obj) {
			if (obj instanceof Ordered) {
				return ((Ordered) obj).getOrder();
			}
			if (obj != null) {
				Class<?> clazz = ((obj instanceof Class) ? (Class<?>) obj : obj.getClass());
				Order order = AnnotationUtils.findAnnotation(clazz, Order.class);
				if (order != null) {
					return order.value();
				}
			}
			return Ordered.LOWEST_PRECEDENCE;
		}

	}

}
